-
Notifications
You must be signed in to change notification settings - Fork 336
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add ping pong functionality #45
Conversation
Oh silly me, we don't actually need to emulate PONG frames (since they are not read), being able to successfully send PING frames should suffice. |
defer client.m.Unlock() | ||
client.conn = c | ||
if client.pinging { | ||
client.newConnChan <- struct{}{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's blocks forever if we never call EnablePinging. Is it ok here? (Maybe I miss some context, sorry.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It shouldn't since when pinging is false, the function won't try to send a signal to newConnChan, and releases the lock when it returns. When pinging is true, there is a goroutine that reads from newConnChan, so writing to it should never block. Am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see. Sorry again :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
S'all good, the more eyes the better!
Hey @c3mb0 - Cheers for the pull request. A few questions;
|
Great catch @sideshow, I did not realize this was added to the http2 package. Correct me if I'm wrong, but it seems like Using this approach could clean up the code a bit, but not my much: After taking a quick peek, from what I can tell, we would still need to create a new Kind of off-topic, but an alternative approach to pinging would be to handle all the pinging transparently within
I haven't tested the code above, it' just to show how shorter and sweeter the non opt-in approach is. The breaking change could be mitigated via gopkg.in. What do you think? |
} | ||
go func() { | ||
// 8 bytes of random data used for PING-PONG, as per HTTP/2 spec. | ||
data := [8]byte{byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256))} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would perhaps look a bit cleaner:
var data [8]byte
rand.Reader.Read(data[:])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, I will change and update the PR.
} | ||
case <-c.newConnChan: | ||
c.m.Lock() | ||
framer = http2.NewFramer(c.conn, c.conn) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't the transport technically dial multiple connections? I guess in practice it doesn't, but is there anything in place stopping it from doing so?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The source code and many production tests reveal that it does not.
1 similar comment
@c3mb0 Apologies for the long delay on reviewing this.
|
@sideshow are you familiar enough with go http2 internals to say in which case it will open multiple http2 connections to oner server? In most of the cases you will have a single connection, except the case when you are doing more than If I got it right, the ping is only needed when you have times without pushing, but you want to have low latency when you start sending notifications again, this will ensure that at least one connection is alive and more will be started if needed. What do you think? |
@nvartolomei Yes it will open new connections when you are doing more than In theory you are correct about only needing Im not sure if the modifications to the go transport layer since this have solved this issue. In any case, the correct place to send ping frames would be the connection pool and not the http client. This is the only place where you have a handle on all the currently active connections and not just the last one. I'm closing this as stale, but have logged an issue/enhancement ticket around creating a better connection pool which could have this functionality #88 |
I have been using PING frames in production for a few months now and it greatly helps with the health of persistent APNs connections. Production clients have not redialed production APNs servers in 1.5 months, and that's only because we deployed around that time. I know this because I've been logging DialTLS. Development clients are another story, since development server connections reset randomly every 1-3 weeks due to Apple's machines moving around and changing IPs.
Keeping a healthy connection also seems decrease the overall amount of network related push errors.
This PR contains a generalized version of the custom solution I've been using. It lacks tests because I am not quite sure how to emulate PONG frames during testing. Any help on that front is appreciated.
It might also make sense to move/use some of this code in ClientManager; That is up for discussion.